package com.zzyl.intercept;

import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.zzyl.constant.CacheConstant;
import com.zzyl.constant.Constants;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.properties.JwtTokenManagerProperties;
import com.zzyl.utils.JwtUtil;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.utils.UserThreadLocal;
import com.zzyl.vo.UserVo;
import io.jsonwebtoken.Claims;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * 管理端用户登录jwt令牌校验和用户权限校验拦截器
 */
@Component
@Slf4j
@RequiredArgsConstructor
public class UserInterceptor implements HandlerInterceptor {


    private final JwtTokenManagerProperties jwtTokenManagerProperties;

    private final StringRedisTemplate redisTemplate;

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    /**
     * 校验jwt
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //判断当前拦截到的是Controller的方法还是其他资源
        if (!(handler instanceof HandlerMethod)) {
            //当前拦截到的不是动态方法，直接放行
            return true;
        }

        //1、从请求头中获取令牌
        String token = request.getHeader(Constants.TOKEN);

        try {
            log.info("jwt校验:{}", token);
            //解析令牌
            Claims claims = JwtUtil.parseJWT(jwtTokenManagerProperties.getBase64EncodedSecretKey(), token);
            String resultJson = String.valueOf(claims.get(Constants.TOKEN));
            //将用户信息转为对象

            UserVo userVo = JSONUtil.toBean(resultJson, UserVo.class);
            log.info("当前用户：{}", userVo);
            //从redis中获取用户对应权限列表
            String urlJson = redisTemplate.opsForValue().get(CacheConstant.PUBLIC_ACCESS_URLS + userVo.getId());
            //判断是否为空
            if (ObjectUtil.isEmpty(urlJson)){
                throw new BaseException(BasicEnum.LOGIN_LOSE_EFFICACY);
            }
            List<String> pathList = JSONUtil.toList(urlJson, String.class);
            //与当前登录用户访问的权限与白名单比对
            String method = request.getMethod();
            String requestURI = request.getRequestURI();
            //用户当前访问的url
            String userPath = method + requestURI;
            //遍历
            for (String path : pathList) {
                //判断当前用户访问的url是否在白名单中
                if (antPathMatcher.match(userPath,path)){
                    //权限校验成功则将用户信息存入线程
                    UserThreadLocal.setSubject(urlJson);
                    //3、通过，放行
                    return true;
                }
            }

            throw new BaseException(BasicEnum.SECURITY_ACCESSDENIED_FAIL);

        } catch (Exception ex) {
            //4、不通过，响应401状态码
            response.setStatus(401);
            return false;
        }
    }

    /**
     * 释放线程
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //释放线程
        UserThreadLocal.removeSubject();
    }
}
